﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Money.GUI
{
    // TODO look at adding an undo option
    // TODO should pressing Enter move us to a new record?
    // TODO we need to handle multiple selections somewhere, grey out fields that are different?
    // TODO tidy icon so that icons is always left aligned no matter which is displayed
    // TODO need a way of getting type and account values when used in a form

    public partial class TransactionGUI : UserControl
    {
        public enum FormMode
        {
            Edit,
            LatchedEdit,
            View,
            InstantEdit,
            Form,
            New
        };

        public event EventHandler<EventArgs> TransactionChanged;
        public event EventHandler<EventArgs> TransactionAdded;
        public event EventHandler<EventArgs> FinishedEdit;
        public event EventHandler<EventArgs> Cancelled;

        public delegate void AddTransactionDelegate(Transaction transaction);
        public AddTransactionDelegate AddTransaction;

        public delegate string GetTransactionDetailDelegate(Transaction transaction);
        public GetTransactionDetailDelegate GetTransactionDetail;

        private bool _instantUpdate = true;
        private FormMode _mode = FormMode.View;

        public FormMode Mode
        {
            get 
            { 
                return _mode; 
            }
            set 
            { 
                _mode = value;
                ConfigureForm(_mode);
            }
        }

        private Transaction _originalTransaction;
        private Transaction _transaction;
        private ToolTip _toolTip = new ToolTip();

        private Transaction _undoBuffer;

        public bool InstantUpdate
        {
            get 
            { 
                return _instantUpdate; 
            }
            set 
            { 
                _instantUpdate = value;
                DisplayUpdateMode();
            }
        }

        public Transaction Transaction
        {
            get
            {
                return _transaction;
            }
            set
            {
                if (value != null)
                {
                    ConfigureAccountSelection();
                    _transaction = value;
                    SetTransaction(_transaction);
                    _undoBuffer = _transaction.Copy();
                }
                else
                {
                    ClearForm();
                }
            }
        }

        public void Undo()
        {
            bool instantUpdate = _instantUpdate;

            if (_undoBuffer != null)
            {
                // Disable instant update while we do an Undo
                _instantUpdate = false;

                _transaction.CopyFrom(_undoBuffer);

                // TODO decide on Undo strategy
                SetTransaction(_undoBuffer);

                // Restore to previous instant update mode
                _instantUpdate = instantUpdate;

                OnTransactionChanged();
            }
        }

        public TransactionGUI()
        {
            InitializeComponent();

            _type.DataSource = Enum.GetValues(typeof(Transaction.TransactionType));

            ConfigureAccountSelection();

            ConfigureForm(_mode);
        }

        private void ConfigureAccountSelection()
        {
            if (MoneyDoc.Instance != null)
            {
                if (MoneyDoc.Instance.Accounts != null)
                {
                    _account.Items.Clear();
                    _transferDestination.Items.Clear();

                    foreach (Account account in MoneyDoc.Instance.Accounts)
                    {
                        _account.Items.Add(account.Name);
                        _transferDestination.Items.Add(account.Name);
                    }
                }
            }
        }

        private void ConfigureForm(FormMode mode)
        {
            switch(mode)
            {
                case FormMode.View:
                    _cancelButton.Text = "New";
                    _cancelButton.Visible = true;
                    _saveButton.Visible = false;
                    _latchedEditMode.Visible = false;
                    break;

                case FormMode.InstantEdit:
                case FormMode.Edit:
                    _cancelButton.Text = "Cancel";
                    _cancelButton.Visible = true;
                    _saveButton.Text = "Save";
                    _saveButton.Visible = true;
                    _latchedEditMode.Visible = false;
                    break;

                case FormMode.New:
                    _cancelButton.Text = "Cancel";
                    _cancelButton.Visible = true;
                    _saveButton.Text = "Add";
                    _saveButton.Visible = true;
                    _latchedEditMode.Visible = false;
                    break;

                case FormMode.LatchedEdit:
                    _cancelButton.Text = "Cancel";
                    _cancelButton.Visible = true;
                    _saveButton.Text = "Save";
                    _saveButton.Visible = true;
                    _latchedEditMode.Visible = true;
                    break;

                case FormMode.Form:
                    _cancelButton.Text = "Cancel";
                    _cancelButton.Visible = true;
                    _saveButton.Text = "OK";
                    _saveButton.Visible = true;
                    _latchedEditMode.Visible = false;
                    _instantUpdate = false;
                    break;
            }

            if (GetTransactionDetail == null)
            {
                _infoIcon.Visible = false;
            }
            else
            {
                _infoIcon.Visible = true;
            }

            DisplayUpdateMode();
            SetControlsEnabled(_mode != FormMode.View);

        }

        private void DisplayUpdateMode()
        {
            if (_instantUpdate)
            {
                _instantEditMode.Visible = true;
            }
            else
            {
                _instantEditMode.Visible = false;
            }
        }

        public void ClearForm()
        {
            _mode = FormMode.View;

            _amount.Text = null;
            _categoryGroup.Text = "";
            _transactionNumber.Text = "";
            _description.Text = "";
            _comment.Text = "";

            _date.Value = DateTime.Now;

            _category.Text = "";
            _category.SelectedValue = -1;
            _type.Text = "";
            _type.SelectedIndex = -1;
            _account.Text = "";
            _account.SelectedIndex = -1;
            _transferDestination.Text = "";

            ConfigureTypeOptions();

            ConfigureForm(_mode);
        }

        public void SetTransaction(Transaction transaction)
        {
            if (transaction != null)
            {
                if (_mode == FormMode.New)
                {
                    DialogResult result = MessageBox.Show("Cancel new transaction creation", "Cancel new transaction"
                                                            , MessageBoxButtons.YesNo
                                                            , MessageBoxIcon.Question
                                                            , MessageBoxDefaultButton.Button1);

                    if (result == DialogResult.No)
                    {
                        return;
                    }
                }

                // TODO setting these values here is setting the Dirty flag if it is not set at present, need to resolve this for Undo to work as expected.
                _amount.Text = transaction.Amount.ToString();
                _categoryGroup.Text = Categories.CategoryList.GetFullCategoryName(transaction.CategoryId);
                _transactionNumber.Text = transaction.TransactionNo.ToString();
                _description.Text = transaction.Description;
                _comment.Text = transaction.Comment;

                _date.Value = transaction.Date;

                _category.Text = transaction.Category;
                _category.SelectedValue = transaction.CategoryId;
                _type.Text = transaction.Type.ToString();
                _account.Text = transaction.Account;
                _transferDestination.Text = transaction.DestinationAccount;

                ConfigureTypeOptions();

                ConfigureForm(_mode);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void ConfigureTypeOptions()
        {
            if (_type.Text == Transaction.TransactionType.Transfer.ToString())
            {
                lblDestination.Visible = true;
                _transferDestination.Visible = true;
            }
            else
            {
                lblDestination.Visible = false;
                _transferDestination.Visible = false;
            }
        }

        private void SetControlsEnabled(bool state)
        {
            foreach (Control control in this._transactionContainer.Controls)
            {
                switch (control.GetType().Name)
                {
                    case "DateTimePicker":
                    case "ComboBox":
                    case "CategoryComboBox":
                    case "TextBox":
                        control.Enabled = state;
                        break;
                }
            }
        }

        private void _cancelButton_Click(object sender, EventArgs e)
        {
            switch (_mode)
            {
                case FormMode.Edit:
                    SetTransaction(_transaction);
                    _mode = FormMode.View;
                    OnCancelled();
                    break;

                case FormMode.Form:
                    OnCancelled();
                    break;

                case FormMode.LatchedEdit:
                    SetTransaction(_transaction);
                    OnCancelled();
                    break;

                case FormMode.New:
                    _mode = FormMode.View;
                    SetTransaction(_originalTransaction);
                    break;

                case FormMode.View:
                    _originalTransaction = _transaction;
                    _transaction = new Transaction();
                    SetTransaction(_transaction);
                    _mode = FormMode.New;
                    break;
            }

            ConfigureForm(_mode);
        }

        private void _saveButton_Click(object sender, EventArgs e)
        {
            SaveTransaction();
        }

        private void SaveTransaction()
        {
            Transaction.Amount = Convert.ToDecimal(_amount.Text);
            Transaction.Account = _account.Text;
            Transaction.Date = _date.Value;
            Transaction.Description = _description.Text;
            Transaction.Comment = _comment.Text;
            Transaction.DestinationAccount = _transferDestination.Text;
            Transaction.CategoryId = _category.SelectedCategory.Id;

            Transaction.TransactionType type = (Transaction.TransactionType)Enum.Parse(typeof(Transaction.TransactionType), _type.Text);
            Transaction.Type = type;

            switch (_mode)
            {
                case FormMode.Edit:
                    // we now leave edit mode and go back to view mode
                    _mode = FormMode.View;
                    OnTransactionChanged();
                    break;

                case FormMode.Form:
                    OnFinishedEdit();
                    break;

                case FormMode.LatchedEdit:
                    OnTransactionChanged();
                    break;

                case FormMode.New:
                    OnAddTransaction();
                    _mode = FormMode.View;
                    OnTransactionAdded();
                    break;

                case FormMode.View:
                    throw new InvalidOperationException();
            }
            
            ConfigureForm(_mode);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="editModeLatch"></param>
        public void EnterEditMode(bool editModeLatch)
        {
            if (editModeLatch)
            {
                _mode = FormMode.LatchedEdit;
            }
            else
            {
                _mode = FormMode.Edit;
            }

            ConfigureForm(_mode);
        }

        /// <summary>
        /// 
        /// </summary>
        public void LeaveEditMode()
        {
            _mode = FormMode.View;

            ConfigureForm(_mode);
        }


        private void TransactionGUI_KeyPress(object sender, KeyPressEventArgs e)
        {

        }

        private void transactionContainer_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {

        }

        private void TransactionGUI_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {

        }

        private void TransactionGUI_Enter(object sender, EventArgs e)
        {
            _transactionContainer.Focus();
        }

        private void transactionContainer_Enter(object sender, EventArgs e)
        {
            // TODO cant remember why this was added but causing issues
            //_comment.Focus();
        }

        private void _categoryGroup_SelectionChangeCommitted(object sender, EventArgs e)
        {
            Category selectedCategory = _category.SelectedCategory;

            if (selectedCategory != null && selectedCategory.Id != _transaction.CategoryId)
            {
                _categoryGroup.Text = Categories.CategoryList.GetFullCategoryName(selectedCategory.ParentId);

                if (_instantUpdate)
                {
                    Transaction.CategoryId = selectedCategory.Id;
                    OnTransactionChanged();
                }
            }
        }

        private void _type_SelectionChangeCommitted(object sender, EventArgs e)
        {
            Transaction.TransactionType type = (Transaction.TransactionType)Enum.Parse(typeof(Transaction.TransactionType), _type.Text);

            ConfigureTypeOptions();

            if (_instantUpdate)
            {
                if (type != Transaction.Type)
                {
                    Transaction.Type = type;
                    OnTransactionChanged();
                }
            }

            ConfigureValidTransferAccounts();
        }

        private void _infoIcon_MouseEnter(object sender, EventArgs e)
        {
            if (GetTransactionDetail != null)
            {
                _toolTip.AutomaticDelay = 10;
                _toolTip.AutoPopDelay = 600000;
                _toolTip.ToolTipIcon = ToolTipIcon.Info;
                _toolTip.ToolTipTitle = "Original Import Item";
                _toolTip.IsBalloon = true;
                _toolTip.SetToolTip(_infoIcon, GetTransactionDetail(_transaction));
            }
        }

        private void OnAddTransaction()
        {
            if (AddTransaction != null)
            {
                AddTransaction(this.Transaction);
            }
        }

        private void OnFinishedEdit()
        {
            if (FinishedEdit != null)
            {
                FinishedEdit(this, new EventArgs());
            }
        }

        private void OnCancelled()
        {
            if (Cancelled != null)
            {
                Cancelled(this, new EventArgs());
            }
        }

        private void OnTransactionChanged()
        {
            if (TransactionChanged != null)
            {
                TransactionChanged(this, new EventArgs());
            }
        }

        private void OnTransactionAdded()
        {
            if (TransactionAdded != null)
            {
                TransactionAdded(this, new EventArgs());
            }
        }

        private void dpDate_ValueChanged(object sender, EventArgs e)
        {
            if (_transaction != null)
            {
                if (_instantUpdate)
                {
                    if (_date.Value != Transaction.Date)
                    {
                        Transaction.Date = _date.Value;
                        OnTransactionChanged();
                    }
                }
            }
        }

        private void tbDescription_TextChanged(object sender, EventArgs e)
        {
            if (_instantUpdate)
            {
                if (!_description.Text.Equals(Transaction.Description))
                {
                    Transaction.Description = _description.Text;
                    OnTransactionChanged();
                }
            }
        }

        private void cbSubCategory_SelectedValueChanged(object sender, EventArgs e)
        {

        }

        private void _account_SelectedValueChanged(object sender, EventArgs e)
        {
            if (_instantUpdate && Transaction != null)
            {
                if (Transaction.Account != _account.Text)
                {
                    Transaction.Account = _account.Text;
                    OnTransactionChanged();
                }
            }

            ConfigureValidTransferAccounts();
        }

        private void ConfigureValidTransferAccounts()
        {
            if (_type.Text.Equals(Transaction.TransactionType.Transfer.ToString()))
            {
                _transferDestination.Items.Clear();
                var destinationAccounts = (from account in MoneyDoc.Instance.Accounts where !account.Name.Equals(_account.Text) select account.Name).ToArray();
                _transferDestination.Items.AddRange(destinationAccounts);
            }
        }

        private void _amount_TextChanged(object sender, EventArgs e)
        {
            decimal amount = Transaction.Amount;
            decimal newAmount = amount;

            try
            {
                newAmount = Convert.ToDecimal(_amount.Text);
            }
            catch
            {
                int textPosition = _amount.SelectionStart;
                if (textPosition < _amount.TextLength)
                {
                    textPosition -= 1;
                }

                // if conversion fails then we stick with what we have
                _amount.Text = amount.ToString();
                _amount.SelectionStart = textPosition;
            }

            if (_instantUpdate)
            {
                if (newAmount != amount)
                {
                    Transaction.Amount = newAmount;
                    OnTransactionChanged();
                }
            }
        }

        private void _comment_TextChanged(object sender, EventArgs e)
        {
            if (_instantUpdate)
            {
                if (!_comment.Text.Equals(Transaction.Comment))
                {
                    Transaction.Comment = _comment.Text;
                    OnTransactionChanged();
                }
            }
        }

        private void _transferDestination_SelectedValueChanged(object sender, EventArgs e)
        {
            string destinationAccount = _transferDestination.Text;

            if (_instantUpdate)
            {
                if (_transaction.DestinationAccount == null || (!_transaction.DestinationAccount.Equals(_transferDestination.Text)))
                {
                    _transaction.DestinationAccount = _transferDestination.Text;
                    OnTransactionChanged();
                }
            }
        }

        private void TransactionGUI_KeyDown(object sender, KeyEventArgs e)
        {

        }

        private void _infoIcon_Click(object sender, EventArgs e)
        {

        }
    }
}
